//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.common.notification;

import java.util.ArrayList;
import java.util.List;

import com.saasovation.common.domain.model.DomainEvent;
import com.saasovation.common.event.EventStore;
import com.saasovation.common.event.StoredEvent;

/**
 * 通知日志工厂
 * @author learn
 */
public class NotificationLogFactory {

    // this could be a configuration
    private static final int NOTIFICATIONS_PER_LOG = 20;
    /**
     * 事件存储
     */
    private EventStore eventStore;

    /**
     * 获取每个日志的通知
     * @return                      返回整型
     */
    public static int notificationsPerLog() {
        return NOTIFICATIONS_PER_LOG;
    }

    /**
     * 由事件存储构造通知日志工厂
     * @param anEventStore          事件存储
     */
    public NotificationLogFactory(EventStore anEventStore) {
        super();

        this.setEventStore(anEventStore);
    }

    /**
     * 创建当前通知日志
     * @return                      返回通知日志
     */
    public NotificationLog createCurrentNotificationLog() {
        return this.createNotificationLog(
                this.calculateCurrentNotificationLogId(eventStore));
    }

    /**
     * 创建通知日志
     * @param aNotificationLogId    通知日志ID
     * @return                      返回通知日志
     */
    public NotificationLog createNotificationLog(
            NotificationLogId aNotificationLogId) {

        // 获取存储事件数
        long count = this.eventStore().countStoredEvents();

        // 由通知日志ID和清单数，建立通知日志信息
        NotificationLogInfo info = new NotificationLogInfo(aNotificationLogId, count);
        // 创建通知日志
        return this.createNotificationLog(info);
    }

    /**
     * 计算当前通知日志ID
     * @param anEventStore          事件存储
     * @return                      返回通知日志信息
     */
    private NotificationLogInfo calculateCurrentNotificationLogId(
            EventStore anEventStore) {
        // 获取存储事件数
        long count = anEventStore.countStoredEvents();
        // 余数
        long remainder = count % NOTIFICATIONS_PER_LOG;
        // 如果余数等于零 且 事件数大于零，则重置为NOTIFICATIONS_PER_LOG
        if (remainder == 0 && count > 0) {
            remainder = NOTIFICATIONS_PER_LOG;
        }

        // 范围的最低值
        long low = count - remainder + 1;

        // ensures a minted id value even though there may
        // not be a full set of notifications at present
        // 范围的最高值
        long high = low + NOTIFICATIONS_PER_LOG - 1;

        return new NotificationLogInfo(new NotificationLogId(low, high), count);
    }

    /**
     * 创建通知日志
     * @param aNotificationLogInfo   通知日志信息
     * @return                       返回通知日志
     */
    private NotificationLog createNotificationLog(
            NotificationLogInfo aNotificationLogInfo) {
        // 从事件存储获取期间的所有存储事件清单
        List<StoredEvent> storedEvents =
            this.eventStore().allStoredEventsBetween(
                    aNotificationLogInfo.notificationLogId().low(),
                    aNotificationLogInfo.notificationLogId().high());

        // 获取存档指标
        boolean archivedIndicator =
                aNotificationLogInfo.notificationLogId().high() < aNotificationLogInfo.totalLogged();

        // 获取下一个通知日志ID
        NotificationLogId next = archivedIndicator ?
                aNotificationLogInfo.notificationLogId().next(NOTIFICATIONS_PER_LOG) :
                null;
        // 获取上一下通知日志ID
        NotificationLogId previous =
                aNotificationLogInfo.notificationLogId().previous(NOTIFICATIONS_PER_LOG);
        // 建立通知日志
        NotificationLog notificationLog =
            new NotificationLog(
                    aNotificationLogInfo.notificationLogId().encoded(),
                    NotificationLogId.encoded(next),
                    NotificationLogId.encoded(previous),
                    this.notificationsFrom(storedEvents),
                    archivedIndicator);

        return notificationLog;
    }

    /**
     * 从存储事件清单获取通知清单
     * @param aStoredEvents   存储事件清单
     * @return
     */
    private List<Notification> notificationsFrom(List<StoredEvent> aStoredEvents) {
        // 建立通知清单
        List<Notification> notifications =
            new ArrayList<Notification>(aStoredEvents.size());
        // 遍历所有存储事件清单，逐一处理
        for (StoredEvent storedEvent : aStoredEvents) {
            // 把存储事件转换成领域事件
            DomainEvent domainEvent = storedEvent.toDomainEvent();

            // 根据事件ID和领域事件，建立通知
            Notification notification =
                new Notification(storedEvent.eventId(), domainEvent);
            // 追加到通知清单
            notifications.add(notification);
        }

        return notifications;
    }

    /**
     * 获取事件存储
     * @return              返回事件存储
     */
    private EventStore eventStore() {
        return eventStore;
    }

    /**
     * 设置事件存储
     * @param anEventStore  事件存储
     */
    private void setEventStore(EventStore anEventStore) {
        this.eventStore = anEventStore;
    }
}
